None.
This invention was not developed in conjunction with any Federally sponsored contract.
Not applicable.
1. Field of the Invention
This invention pertains to the arts of computer program technologies, program execution methods, and multitask program management techniques. More particularly, this invention relates to program stack management for computer program operating systems and computer program compilers.
2. Description of the Related Art
A computer program process may xe2x80x9ccallxe2x80x9d another process or subroutine to be executed on the same or by the same processor which is executing the xe2x80x9ccallingxe2x80x9d process. When the xe2x80x9ccallxe2x80x9d operation or instruction is executed, the execution of the xe2x80x9ccallingxe2x80x9d process is halted at the point of the xe2x80x9ccallxe2x80x9d instruction, and the xe2x80x9ccalledxe2x80x9d process or subroutine is initialized and executed.
In order to allow the xe2x80x9ccalledxe2x80x9d process or subroutine to fully utilize all or part of the processor""s computing resources, including the processor""s registers, and in order to preserve the context of the xe2x80x9ccallingxe2x80x9d process, a program stack is used. A stack in a computer system is a data structure disposed in a computer-readable memory which dynamically stores variable and processor state information between process calls and returns.
The stack has a xe2x80x9cfirst-in-last-outxe2x80x9d (xe2x80x9cFILOxe2x80x9d) structure. As data is placed into it, or xe2x80x9cpushed onto the stackxe2x80x9d, older data is pushed further into the structure. As shown in FIG. 1, a processor (1) has a bi-directional data bus (3) to the stack memory (2). Often, the stack memory and bi-directional data bus is integrated into the processor microchip itself.
At the assembly language level, most processors have a xe2x80x9cpushxe2x80x9d operation which will copy data from a processor register or memory location to the top of the stack, and will adjust the top-of-stack pointer appropriately. Conversely, a xe2x80x9cpopxe2x80x9d operation will copy data from the top of the stack into a processor register or memory location, and will adjust the top-of-stack pointer. Some processors implement the stack in an incrementing address fashion, such that xe2x80x9cpushesxe2x80x9d result in increases in the stack pointer value, and xe2x80x9cpopsxe2x80x9d result in decreases in the stack pointer value. Other processors may take the opposite approach, but achieve identical functionality, including adding data to the bottom of the stack rather than the top of stack. These variations of conventions for stack handling and construction are well understood in the art.
When the processor executes a xe2x80x9ccallxe2x80x9d operation, an automatic xe2x80x9cpushxe2x80x9d of the contents of the program counter register is made, thereby storing the return address of the calling process on the stack. When the called operation or subroutine executes a xe2x80x9creturnxe2x80x9d operation, an automatic xe2x80x9cpopxe2x80x9d of the top of the stack contents is made to the program counter, which restores the program counter to the return address of the calling process or routine.
When a function call is made from a high-level language (xe2x80x9cHLLxe2x80x9d), such as xe2x80x9cCxe2x80x9d, the compiler generates assembly language representing the higher-level statements in the HLL to implement the source code into assembly code. When a function or routine call includes arguments to be passed to and returned from the function or routine, the compiler injects not only the assembly xe2x80x9ccallxe2x80x9d opcode into the assembly language, but also a block of code to store the argument values on the stack, as well. This block or sequence of assembly language instructions may include several xe2x80x9cpushesxe2x80x9d, or special write or store operations, to the stack. Also, in typical HLL compilers, the processor context may be stored on the stack as part of the function or subroutine call operation. The context of the processor typically includes volatile processor register values, such as interrupt control registers, indirect addressing registers and other control registers. A processor-specific example of a volatile register which is stored as part of the context save is the ECX register of an Intel x86 processor.
When a subroutine is initialized as the result of a xe2x80x9ccallxe2x80x9d operation, it typically sets up a local stack frame for data variables which are local to the subroutine. These local variables are usually addressed by an offset from the beginning of the local stack frame, commonly stored in a xe2x80x9cbasexe2x80x9d register, such as BP or EBP on an Intel x86 processor. In addition, sometimes temporary or transient values are stored on the stack dynamically within the subroutine, rather than including them as part of the local stack frame.
As shown in FIG. 2, if executing Process A (20) calls Process B, the return address and context of the processor for Process A is pushed (21) onto the stack, and the pointer for the top of the stack is moved to the xe2x80x9ctopxe2x80x9d of the memory containing the Process A context data (203). Further, space is created on the xe2x80x9ctopxe2x80x9d of the stack for Process B""s local variables and the top of stack is adjusted (204). Execution of Process B then takes place (22), until Process B calls Process C. At this time, the processor pushes the processor context onto the stack for Process B""s current state (23), and moves the top of stack pointer appropriately (205) then the top of stack is adjusted to make room for process C""s local variables (206) to make room for Process C""s local variables.
When Process C completes its execution, it will typically execute a xe2x80x9creturnxe2x80x9d operation, which causes the processor to move the top of stack pointer down to Process B""s area (202), and to xe2x80x9cpopxe2x80x9d or read the processor register values from the stack area for Process B (25). The register values are restored to B""s state, and the software for Process B is resumed execution (22) from the point of the xe2x80x9ccallxe2x80x9d operation in Process B""s instruction code. When Process B completes its execution, a similar xe2x80x9creturnxe2x80x9d operation is made, the top of stack pointer is moved downward (202), and the context for Process A is restored (26) so that Process A""s execution (20) can resume and the point of the xe2x80x9ccallxe2x80x9d instruction in Process A""s code. This type of general stack operation is well known within the art.
It well known within the art that the stack can be implemented in computer memory in linear, contiguous memory space, or it may be implemented as a distributed linked list of sections of memory space, while still providing the same functionality as described supra. Additionally, some computer systems xe2x80x9cpushxe2x80x9d data onto the stack in incrementing address values (which results in xe2x80x9cpopsxe2x80x9d decreasing the address values), while other systems xe2x80x9cpushxe2x80x9d data onto the stack in decrementing address values (and xe2x80x9cpopsxe2x80x9d increase the address value). Further, systems which run operating systems may contain the stack management functionality in operating system modules in order to free the application software from stack management, which in simpler systems, stack management may be handled directly by the application software modules. These variations of the pointer management do not affect the FILO operation of the stack, and are merely alternate implementations of the same structure.
Upon closer examination of this type of stack management process, a potential operation and security problem is apparent. When an application starts execution, the memory used for the stack segment is typically initialized to all zeros. However, after some period of execution, there is stale or non-zero data on the unused portion of the stack Table 1 shows a example calling sequence to be used to illustrate the problem.
In this example, if Proc A calls Proc B, which then calls Proc C, which then returns to Proc B, and which then returns to Proc A, there is stale information on the unused portion of the stack, such as the return address where Proc A called Proc B, the return address where Proc B called Proc C, any passed in parameters, some local (stack) data variables, etc.
This is problematic in three ways. First, this may represent a data security issue. After returning from a procedure call, even a call to a procedure in a dynamically linked library, all of the local data variables still exist on the stack. If the called procedure has done any sort of password decryption, built any filenames, etc., there is a potential for this information to be exposed to other processes because the data is still available in the memory.
Second, the stale data may present confusing trace information when analyzing local variables for program debug and diagnosis. When analyzing trace data where stack snapshots have been taken, or when debugging an application, it is not always clear if the local data variables contain stale data from a previous call, or have actually been initialized by the procedure.
Third, when analyzing the call-chain for system debug, the stale data may present misleading information. After some period of execution, it is not uncommon to have a developer attempt to determine the sequence of procedures which have called to get the application to the current point of execution. If the compiler saves and restores a base stack pointer (EBP or BP on the Intel X86 platform), then the developer can trace backwards using the base stack pointer. However, some compilers use special optimization (e.g. Visual Age""s Optilink calling convention) which do not save/restore the base stack pointer. Also, some assembler language procedures do not save/restore the base stack pointer. If a procedure of this type has been called, then the base stack pointer chain on the stack is broken, and the developer cannot follow the call-chain back to the origin. In this case, the developer will typically perform the time-consuming process of dumping the stack and tediously analyze the stack in the hopes of reconstructing the call-chain. This is both time-consuming and not always successful, particularly in the case of circular or recursive calls (A calls B which calls A), or multiple branches leading to the same node (A calls B which calls C, but A can also call C directly).
Therefore, there is a need in the art for a method which reduces the likelihood that potentially sensitive data in not residually present in computer stack memory following the execution of a software process. Further, there exists a need in the art for a method which provides accurate and clear data and call-sequence evidence in stack memory to aid in effective program development and system operation debugging and diagnosis.